#include <stdlib.h>
#include <string.h>

#include "dlist.h"

/* name: dlist_init
 * Initialize the list.
 */
void dlist_init(DList *list, void (*destroy)(void *data))
{
  list->size = 0;
  list->destroy = destroy;
  list->head = NULL;
  list->tail = NULL;
  
  return;
}

/* name: dlist_destroy
 */
void dlist_destroy(DList *list)
{
  void *data;

  /* Remove each element */
  while (dlist_size(list) > 0) {
    if (0 == dlist_remove(list, dlist_tail(list), (void **)&data)
	&& list-> destroy != NULL) {
      /* Call a user-defined function to free */
      /* dynamically allocated data. */
      list->destroy(data);
    }
  }

  /* No operations are allowed now, */
  /* but clear the structure as a precaution */
  memset(list, 0, sizeof(DList));
  
  return;
}

/* name: dlist_ins_next
 */
int dlist_ins_next(DList *list, DListElmt *element, const void *data)
{
  DListElmt *new_element;

  /* Do not allow a NULL element unless the list is empty */
  if (NULL == element && dlist_size(list) !=0)
    return -1;

  /* Allocate storage for the element */
  if (NULL == (new_element = (DListElmt *)malloc(sizeof(DListElmt))))
    return -1;

  /* Insert the new element into the list */
  new_element->data = (void *)data;
  if (0 == dlist_size(list)) {
    /* Handle insertion when the list is empty */
    list->head = new_element;
    list->head->prev = NULL;
    list->head->next = NULL;
    list->tail = new_element;
  } else {
    /* Handle insertion when the list is not empty */
    new_element->next = element->next;
    new_element->prev = element;

    if (NULL == element->next)
      list->tail = new_element;
    else
      element->next->prev = new_element;

    element->next = new_element;
  }

  /* Adjust the size of the list to account for the inserted element */
  list->size++;

  return 0;
}

/* name: dlist_ins_prev
 */
int dlist_ins_prev(DList *list, DListElmt *element, const void *data)
{
  DListElmt *new_element;

  /* Do not allow a NULL element unless the list is empty */
  if (NULL == element && dlist_size(list) != 0)
    return -1;

  /* Allocate storage to be managed by the abstract datatype */
  if (NULL == (new_element = (DListElmt *)malloc(sizeof(DListElmt))))
    return -1;

  /* Insert the new element into the list */

  new_element->data = (void *)data;

  if (0 == dlist_size(list)) {
    /* Handle insertion when the list is empty */
    list->head = new_element;
    list->head->next = NULL;
    list->head->prev = NULL;
    list->tail = new_element;
  } else {
    /* Handle insertion when the list is not empty */
    new_element->next = element;
    new_element->prev = element->prev;

    if (NULL == element->prev)
      list->head = new_element;
    else
      element->prev->next = new_element;

    element->prev = new_element;
  }

  /* Adjust the size of the list to account for the new element */
  list->size++;

  return 0;
}

/* name: dlist_remove
 */
int dlist_remove(DList *list, DListElmt *element, void **data)
{
  /* Do not allow a NULL element or removal from an empty list */
  if (NULL == element || 0 == dlist_size(list))
    return -1;

  /* Remove the element from the list */
  *data = element->data;

  if (element == list->head) {
    /* Handle removal from the head of the list */
    list->head = element->next;

    if (NULL == list->head)
      list->tail = NULL;
    else
      element->next->prev = NULL;
    
  } else {
    /* Handle removal from other than the head of the list */
    element->prev->next = element->next;

    if (NULL == element->next)
      list->tail = element->prev;
    else
      element->next->prev = element->prev;
  }

  /* Free the storage allocated by the abstract datatype */
  free(element);

  /* Adjust the size of the list to account for the removed element */
  list->size--;

  return 0;
}
